// RSA加密示例 - 生成密钥对、加密和解密
const crypto = require("crypto")
const fs = require("fs")
const path = require("path")
const express = require("express")

// 1. 生成RSA密钥对（公钥和私钥）
function generateRSAKeyPair() {
  console.log("正在生成RSA密钥对...")

  // 生成RSA密钥对，使用2048位密钥长度
  const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
    modulusLength: 2048, // 密钥长度
    publicKeyEncoding: {
      type: "spki", // 公钥格式
      format: "pem" // PEM格式输出
    },
    privateKeyEncoding: {
      type: "pkcs8", // 私钥格式
      format: "pem" // PEM格式输出
    }
  })

  console.log("RSA密钥对生成完成！")
  return { publicKey, privateKey }
}

// 保存密钥到文件
function saveKeysToFile(publicKey, privateKey) {
  console.log("正在保存密钥到文件...")

  // 确保目录存在
  const keysDir = path.join(__dirname, "keys")
  if (!fs.existsSync(keysDir)) {
    fs.mkdirSync(keysDir, { recursive: true })
  }

  // 保存公钥
  fs.writeFileSync(path.join(keysDir, "public.pem"), publicKey)
  console.log(`公钥已保存到: ${path.join(keysDir, "public.pem")}`)

  // 保存私钥
  fs.writeFileSync(path.join(keysDir, "private.pem"), privateKey)
  console.log(`私钥已保存到: ${path.join(keysDir, "private.pem")}`)
}

// 从文件读取密钥
function loadKeysFromFile() {
  console.log("正在从文件读取密钥...")

  const keysDir = path.join(__dirname, "keys")

  try {
    // 读取公钥
    const publicKey = fs.readFileSync(path.join(keysDir, "public.pem"), "utf8")

    // 读取私钥
    const privateKey = fs.readFileSync(path.join(keysDir, "private.pem"), "utf8")

    console.log("密钥读取完成！")
    return { publicKey, privateKey }
  } catch (error) {
    console.error("读取密钥文件失败:", error.message)
    return null
  }
}

// 2. 使用公钥加密数据
function encryptWithRSA(publicKey, data) {
  console.log("正在加密数据...")

  // 使用公钥加密，使用OAEP填充
  const encryptedData = crypto.publicEncrypt(
    {
      key: publicKey,
      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
    },
    // 转换为Buffer，如果不是Buffer类型
    Buffer.from(data, "utf8")
  )

  console.log("数据加密完成！")
  // 返回Base64编码的加密数据
  return encryptedData.toString("base64")
}

// 3. 使用私钥解密数据
function decryptWithRSA(privateKey, encryptedData) {
  console.log("正在解密数据...")

  try {
    // 解密前将Base64编码的加密数据转换为Buffer
    const encryptedBuffer = Buffer.from(encryptedData, "base64")

    // 首先尝试PKCS1填充解密（JSEncrypt默认使用的填充）
    try {
      console.log("尝试使用PKCS1填充...")
      const decryptedData = crypto.privateDecrypt(
        {
          key: privateKey,
          padding: crypto.constants.RSA_PKCS1_PADDING
        },
        encryptedBuffer
      )
      
      console.log("使用PKCS1填充解密成功！")
      // 检查是否是编码过的数据
      const decryptedStr = decryptedData.toString("utf8")
      
      if (decryptedStr.startsWith("UTF8:")) {
        console.log("检测到UTF8编码标记，进行解码...")
        try {
          // 提取Base64编码部分
          const base64Data = decryptedStr.substring(5)
          // 解码Base64为二进制数据
          const binaryData = Buffer.from(base64Data, "base64")
          // 解码为UTF8文本
          return binaryData.toString("utf8")
        } catch (decodeError) {
          console.error("UTF8解码失败:", decodeError)
          return decryptedStr // 如果解码失败，返回原始字符串
        }
      } else {
        return decryptedStr
      }
    } catch (pkcs1Error) {
      console.error("PKCS1解密失败:", pkcs1Error.message)

      // 尝试使用OAEP填充解密（Forge库使用的填充）
      try {
        console.log("尝试使用OAEP填充...")
        const decryptedData = crypto.privateDecrypt(
          {
            key: privateKey,
            padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
          },
          encryptedBuffer
        )

        console.log("使用OAEP填充解密成功！")
        // 检查是否是编码过的数据
        const decryptedStr = decryptedData.toString("utf8")
        
        if (decryptedStr.startsWith("UTF8:")) {
          console.log("检测到UTF8编码标记，进行解码...")
          try {
            // 提取Base64编码部分
            const base64Data = decryptedStr.substring(5)
            // 解码Base64
            const binaryData = Buffer.from(base64Data, "base64")
            return binaryData.toString("utf8")
          } catch (decodeError) {
            console.error("UTF8解码失败:", decodeError)
            return decryptedStr // 如果解码失败，返回原始字符串
          }
        } else {
          return decryptedStr
        }
      } catch (oaepError) {
        console.error("OAEP解密失败:", oaepError.message)
        
        // 再尝试NO_PADDING方式，并进行手动填充处理
        try {
          console.log("尝试使用NO_PADDING和手动填充处理...")
          const decryptedData = crypto.privateDecrypt(
            {
              key: privateKey,
              padding: crypto.constants.RSA_NO_PADDING
            },
            encryptedBuffer
          )
          
          // 尝试从解密结果中提取有效数据
          // 首先去除开头的无效字节
          let cleanedBytes = [...decryptedData]
          // 跳过开头的0和潜在的格式字节
          let startIdx = 0
          while (startIdx < cleanedBytes.length && cleanedBytes[startIdx] === 0) {
            startIdx++
          }
          // 跳过格式字节
          if (startIdx < cleanedBytes.length && cleanedBytes[startIdx] === 2) {
            startIdx++
          }
          
          // 跳过填充字节直到第一个0
          while (startIdx < cleanedBytes.length && cleanedBytes[startIdx] !== 0) {
            startIdx++
          }
          // 再跳过0字节本身
          if (startIdx < cleanedBytes.length) {
            startIdx++
          }
          
          if (startIdx < cleanedBytes.length) {
            // 提取有效负载
            const payload = Buffer.from(cleanedBytes.slice(startIdx))
            const str = payload.toString("utf8")
            
            // 检查是否包含UTF8标记
            if (str.includes("UTF8:")) {
              const markerIndex = str.indexOf("UTF8:")
              if (markerIndex >= 0) {
                const base64Part = str.substring(markerIndex + 5)
                try {
                  const binaryData = Buffer.from(base64Part, "base64")
                  return binaryData.toString("utf8")
                } catch (e) {
                  console.error("尝试解码失败:", e)
                }
              }
            }
            
            console.log("使用NO_PADDING和手动处理成功！")
            return str
          }
          
          throw new Error("无法从解密数据中提取有效内容")
        } catch (nopadError) {
          console.error("所有解密方法均失败")
          throw pkcs1Error // 抛出最初的错误
        }
      }
    }
  } catch (error) {
    console.error("解密或解码过程出错:", error)
    throw new Error("解密失败: " + error.message)
  }
}

// 确保密钥存在，如果不存在则创建
function ensureKeysExist() {
  let keys = loadKeysFromFile()
  if (!keys) {
    console.log("密钥不存在，正在创建新密钥...")
    keys = generateRSAKeyPair()
    saveKeysToFile(keys.publicKey, keys.privateKey)
  }
  return keys
}

// 创建Express服务器
const app = express()
const PORT = 8080

// 设置中间件
app.use(express.static(path.join(__dirname, "public")))
app.use(express.json())

// 获取公钥的API
app.get("/api/public-key", (req, res) => {
  const keys = ensureKeysExist()
  res.json({ publicKey: keys.publicKey })
})

// 接收加密数据并解密的API
app.post("/api/decrypt", (req, res) => {
  try {
    const { encryptedData } = req.body
    if (!encryptedData) {
      return res.status(400).json({ error: "缺少加密数据" })
    }

    const keys = ensureKeysExist()
    const decrypted = decryptWithRSA(keys.privateKey, encryptedData)

    res.json({
      success: true,
      originalData: decrypted,
      message: "解密成功"
    })
  } catch (error) {
    console.error("解密失败:", error)
    res.status(500).json({
      success: false,
      error: "解密失败: " + error.message
    })
  }
})

// 确保public目录存在
const publicDir = path.join(__dirname, "public")
if (!fs.existsSync(publicDir)) {
  fs.mkdirSync(publicDir, { recursive: true })
}

// 启动服务器
app.listen(PORT, () => {
  console.log(`服务器已启动，访问 http://localhost:${PORT} 进行测试`)

  // 确保密钥存在
  ensureKeysExist()
})
